<template>
  <div class="lora-manager">
    <!-- 工具栏部分保持不变 -->
    <div class="toolbar">
      <div class="filters">
        <div style="display: flex;gap:10px;">
          <el-input v-stop-comfy-events v-model="state.searchTerm" :placeholder="$t('message.common.search')" clearable @input="filterLoras"
            class="search-input" size="default">
            <template #prefix>
              <el-icon>
                <search />
              </el-icon>
            </template>
          </el-input>
          <el-button class="ml10" size="default" type="danger" @click="clearFilters">{{ $t('message.common.clear') }}</el-button>
          <el-select size="default" v-model="state.sortBy" @change="sortLoras" class="sort-select ml10">
            <el-option :label="$t('name-asc')" value="name-asc"></el-option>
            <el-option :label="$t('name-desc')" value="name-desc"></el-option>
          </el-select>
          <el-button type="primary" @click="refreshList" :loading="state.isLoading" class="refresh-btn" size="default">
          {{ $t('message.common.refresh') }}
          </el-button>
          <el-button type="primary" @click="openLoraDrawer" :loading="state.isLoading" class="refresh-btn" size="default">
            {{ $t('message.lora.loraApply') }}
          </el-button>
          <el-button type="primary" @click="scanAllLora" :loading="taskStatus.loading" class="refresh-btn" size="default">
            ScanLora
          </el-button>
          <el-progress :percentage="taskStatus.progress" style="width: 150px;"/>
          <el-button 
              type="primary" 
              @click="scanAndCompleteCivitai" 
              :loading="civitaiTaskStatus.loading" 
              class="refresh-btn" 
              size="default"
            >
              Scan Civitai
            </el-button>
            <el-progress  :percentage="civitaiTaskStatus.progress" style="width: 150px;"/>
        </div>
        <div class="folder-tags">
          <el-tag v-for="folder in grandparentFolders" :key="folder.path" size="default"
            :type="selectedGrandparent === folder.name ? 'primary' : 'info'" @click="selectGrandparent(folder)"
            class="folder-tag">
            {{ folder.name }} ({{ folder.loraCount }})
          </el-tag>
        </div>
        <div v-if="parentFolders.length" class="folder-filter">
          <span>{{ $t('message.lora.subFolder') }}:</span>
          <div class="folder-tags">
            <el-tag v-for="subfolder in parentFolders" size="default" :key="subfolder.path"
              :type="selectedParent === subfolder.name ? 'primary' : 'info'" @click="selectParent(subfolder)"
              class="folder-tag">
              {{ subfolder.name }} ({{ subfolder.loraCount }})
            </el-tag>
          </div>
        </div>
      </div>
    </div>

    <div class="lora-grid-container" ref="gridContainer" @scroll.passive="handleScroll">
      <div class="lora-grid-phantom" :style="{ height: totalHeight + 'px' }"></div>

      <div class="lora-grid-content" :style="{
        transform: `translateY(${startRow * rowHeight}px)`,
        gridTemplateColumns: `repeat(${columnCount}, 1fr)`
      }">
        <div v-for="lora in visibleLoras" :key="lora.lora_path"
             @contextmenu.prevent="onContextmenu(lora, $event)"
             @click = switchLora(lora.lora_path)
             class="lora-item">
          <div class="image-container">
            <img :src="getLocalImageUrl(lora)" :alt="lora.lora_name" class="lora-image"
                 loading="lazy" @load="handleImageLoad" @error="handleImageError" />
            <div v-if="!lora.lora_image" class="image-placeholder">
              <el-icon>
                <picture />
              </el-icon>
            </div>
          </div>
          <div class="lora-info">
            <div class="lora-name" :title="lora.lora_name">
              {{ lora.lora_name }}
            </div>
            <div class="lora-path">
              {{ lora.grandparent_folder || '' }}
              <span v-if="lora.parent_folder">/ {{ lora.parent_folder }}</span>
              <el-tag v-if="isLoraApplied(lora.lora_path)" size="default" type="danger" class="applied-tag">
                {{ $t('message.common.apply') }}
              </el-tag>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div v-if="state.isLoading" class="loading-indicator">
      <el-icon class="loading-icon">
        <loading />
      </el-icon>
      {{ $t('message.common.loading') }}
    </div>
    <div v-show="preloadMode">
      <el-drawer v-model="showLoraApply" size="50%" :with-header="false">
        <LoraLoader ref="loraLoaderRef" />
      </el-drawer>
    </div>

    <Contextmenu ref="contextmenuRef" :menu-items="state.customMenuItems" :dropdown="state.dropdown"
      @current-contextmenu-click="onCurrentContextmenuClick" />
    <el-upload
      v-show="false"
      ref="loraImageUploader"
      action=""
      :show-file-list="false"
      :before-upload="beforeImageUpload"
      accept="image/*"
    >
      <el-button
        type="primary"
        size="default"
        class="action-btn"
        icon="ele-Upload"
      >
      </el-button>
    </el-upload>
  </div>
</template>

<script setup lang="ts">
import { reactive, computed, onMounted, ref, onUnmounted, watch, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { useConfigStore } from '@renderer/stores/configStore';
import { debounce, throttle } from 'lodash-es';
import Contextmenu from '@renderer/views/common/component/contextmenu.vue'
import LoraLoader from "@renderer/views/random/component/LoraLoader.vue"
import { ipcService } from '@renderer/services/ipcService';
import { useExPrompt } from '@renderer/stores/useExPrompt';
import { loraApi } from '@renderer/api/lora';
import { translateApi } from '@renderer/api/translate';

const contextmenuRef = ref()
const loraLoaderRef = ref()
const loraImageUploader = ref();
const currentReplaceLora = ref("");
const exPrompt = useExPrompt();

interface LoraInfo {
  lora_name: string;
  lora_config: string;
  lora_image: string | null;
  lora_path: string;
  parent_folder: string;
  grandparent_folder: string;
}

interface FolderNode {
  name: string;
  path: string;
  children: FolderNode[];
}

// 文件夹筛选相关状态
const selectedGrandparent = ref('');
const selectedParent = ref('');
const showLoraApply = ref(false)
const preloadMode = ref(false)
const grandparentFolders = ref<EnhancedFolderNode[]>([]);
const parentFolders = ref<EnhancedFolderNode[]>([]);

// 虚拟滚动相关
const gridContainer = ref<HTMLElement | null>(null);
const rowHeight = 250; // 每行高度 (项目高度 + 间隙)
const columnCount = ref(4); // 列数
const scrollTop = ref(0);
const containerHeight = ref(0);
let resizeObserver: ResizeObserver | null = null;

// 增强文件夹节点类型，包含Lora数量
interface EnhancedFolderNode {
  name: string;
  path: string;
  children: EnhancedFolderNode[];
  loraCount: number;
}

interface MenuItem {
  id: string
  txt: string
  icon: string
  action: 'add' | 'metadata' | 'civitai' | 'replace' | 'remove'
}

// 组件状态
const state = reactive({
  loraInfoList: [] as LoraInfo[],
  folderStructure: [] as FolderNode[],
  isLoading: false,
  searchTerm: '',
  sortBy: 'name-asc',
  filteredLoras: [] as LoraInfo[], // 改为手动更新，减少计算属性依赖
  dropdown: { x: 0, y: 0 },
  customMenuItems: [
    { id: 'metadata', txt: '查看元数据', icon: 'ele-Edit', action: 'metadata' },
    { id: 'add', txt: '添加Lora', icon: 'ele-CirclePlus', action: 'add' },
    { id: 'civitai', txt: '获取civitai数据', icon: 'ele-DocumentCopy', action: 'civitai' },
    { id: 'replace', txt: '替换lora封面', icon: 'ele-DocumentCopy', action: 'replace' },
    { id: 'remove', txt: '移除lora', icon: 'ele-Delete', action: 'remove' }
  ] as MenuItem[],
  metadata: {} as Record<string, any>,
  tagFrequency: [] as Array<{ tag: string, frequency: number }>,
  metadataTags: '',
  chooseCount: 10,
  civitaiLoading: false,
  percentage: 0,
  scanLoading: false,
});

const isLoraApplied = (loraName: string): boolean => {
  return exPrompt.loraList.some(lora => lora.lora_name == loraName);
};

// 缓存可见区域数据
const visibleLorasCache = ref<LoraInfo[]>([]);

// 使用ref存储过滤后的Loras，减少计算属性依赖
const filteredLoras = ref<LoraInfo[]>([]);

// 手动更新过滤后的Loras
const updateFilteredLoras = () => {
  let loras = [...state.loraInfoList];

  // 应用文件夹筛选
  if (selectedGrandparent.value) {
    loras = loras.filter(lora => {
      const isInGrandparent =
        lora.grandparent_folder === selectedGrandparent.value ||
        (!lora.grandparent_folder && lora.parent_folder === selectedGrandparent.value);

      if (selectedParent.value) {
        return isInGrandparent && lora.parent_folder === selectedParent.value;
      }
      return isInGrandparent;
    });
  }

  // 应用搜索词过滤
  if (state.searchTerm) {
    const term = state.searchTerm.toLowerCase();
    loras = loras.filter(lora =>
      lora.lora_name.toLowerCase().includes(term) ||
      (lora.parent_folder && lora.parent_folder.toLowerCase().includes(term)) ||
      (lora.grandparent_folder && lora.grandparent_folder.toLowerCase().includes(term))
    );
  }

  // 应用排序
  switch (state.sortBy) {
    case 'name-asc':
      filteredLoras.value = [...loras].sort((a, b) => a.lora_name.localeCompare(b.lora_name));
      break;
    case 'name-desc':
      filteredLoras.value = [...loras].sort((a, b) => b.lora_name.localeCompare(a.lora_name));
      break;
    default:
      filteredLoras.value = loras;
  }
};

const onCurrentContextmenuClick = async ({ menuItem, contextItem }) => {
  switch (menuItem.action) {
    case 'metadata':
      loraLoaderRef.value.getLoraMetaData(contextItem.lora_path)
      break
    case 'add':
      loraLoaderRef.value.addLora(contextItem.lora_path)
      break
    case 'civitai':
      handleLoadCivitaiInfo(contextItem.lora_path)
      break
    case 'replace':
      currentReplaceLora.value = contextItem.lora_path
      nextTick(() => {
        loraImageUploader.value?.$el.querySelector('input[type="file"]')?.click();
      });
      break
    case 'remove':
      loraLoaderRef.value.removeLora(contextItem.lora_path)
      break
  }
}

const switchLora = async (loraPath:string)=>{
  const exists = exPrompt.loraList.some(lora => lora.lora_name === loraPath);
  if (exists) {
    loraLoaderRef.value.removeLora(loraPath)
  }
  else{
    loraLoaderRef.value.addLora(loraPath)
  }
}

const imageVersions = reactive<Record<string, number>>({});

const handleLoadCivitaiInfo = async (loraPath:string)=>{
  state.civitaiLoading = true;
  try{
    const res = await ipcService.getCivitaiMetadata(undefined,loraPath,true);
    console.log(res)
    if (res.success) {
      ElMessage.info(res.message)
      imageVersions[loraPath] = (imageVersions[loraPath] || 0) + 1;
      forceVirtualScrollUpdate();
    }
  }
  catch(error){
    ElMessage.error(JSON.stringify(error))
  }
  finally{
    state.civitaiLoading = false
  }
}

const beforeImageUpload = async (file: File) => {
  try {
    const loraPath = currentReplaceLora.value;
    const result = await ipcService.saveLoraImage(
      file,
      loraPath
    );

    if (result.success) {
      const loraPath = currentReplaceLora.value;
      configStore.updateImageVersion(loraPath.replace(".safetensors",".webp"));
      forceVirtualScrollUpdate();
      ElMessage.success('封面保存成功');
    } else {
      ElMessage.error(`保存失败: ${result.message}`);
    }
  } catch (error) {
    ElMessage.error('上传封面时出错');
    console.error('封面上传错误:', error);
  }
  return false;
};

const forceVirtualScrollUpdate = () => {
  // 通过修改一个临时变量来触发虚拟滚动重新计算
  // 这里使用scrollTop的微小变化来触发重新渲染
  if (gridContainer.value) {
    const originalScrollTop = gridContainer.value.scrollTop;
    gridContainer.value.scrollTop = originalScrollTop + 1;
    nextTick(() => {
      gridContainer.value.scrollTop = originalScrollTop;
    });
  }
};

const onContextmenu = (lora: LoraInfo, e: MouseEvent) => {
  // 立即响应右键点击，避免等待虚拟滚动计算
  const { clientX, clientY } = e
  state.dropdown.x = clientX
  state.dropdown.y = clientY

  // 使用nextTick确保DOM更新后再打开菜单
  nextTick(() => {
    contextmenuRef.value.openContextmenu(lora)
  })
}

// 计算虚拟滚动相关值
const totalRows = computed(() => {
  return Math.ceil(filteredLoras.value.length / columnCount.value);
});

const totalHeight = computed(() => {
  return totalRows.value * rowHeight;
});

const startRow = computed(() => {
  return Math.max(0, Math.floor(scrollTop.value / rowHeight) - 1);
});

const endRow = computed(() => {
  return Math.min(totalRows.value, Math.ceil((scrollTop.value + containerHeight.value) / rowHeight) + 1);
});

const visibleLoras = computed(() => {
  const startIndex = Math.max(0, startRow.value * columnCount.value);
  const endIndex = Math.min(filteredLoras.value.length, (endRow.value + 1) * columnCount.value);

  // 缓存结果以减少重新渲染
  const newVisibleLoras = filteredLoras.value.slice(startIndex, endIndex);

  // 只有当可见项确实改变时才更新缓存
  const isSame = newVisibleLoras.length === visibleLorasCache.value.length &&
                 newVisibleLoras.every((item, index) =>
                   item.lora_path === visibleLorasCache.value[index]?.lora_path);

  if (!isSame) {
    visibleLorasCache.value = newVisibleLoras;
  }

  return visibleLorasCache.value;
});

const configStore = useConfigStore();

// 选择一级文件夹
const selectGrandparent = (folder: EnhancedFolderNode) => {
  if (selectedGrandparent.value === folder.name) {
    selectedGrandparent.value = '';
    selectedParent.value = '';
    parentFolders.value = [];
  } else {
    selectedGrandparent.value = folder.name;
    selectedParent.value = '';
    parentFolders.value = folder.children;
  }
  updateFilteredLoras();
};

// 选择二级文件夹
const selectParent = (subfolder: EnhancedFolderNode) => {
  if (selectedParent.value === subfolder.name) {
    selectedParent.value = '';
  } else {
    selectedParent.value = subfolder.name;
  }
  updateFilteredLoras();
};

// 清除所有筛选
const clearFilters = () => {
  selectedGrandparent.value = '';
  selectedParent.value = '';
  parentFolders.value = [];
  updateFilteredLoras();
};

// 获取Lora列表
const getLoraList = async () => {
  state.isLoading = true;
  try {
    const result = await ipcService.scanLoras(configStore.config.loraPath);
    if (result.success) {
      state.loraInfoList = result.data.loras;
      state.folderStructure = result.data.folderStructure;

      // 增强文件夹节点，添加Lora数量统计
      grandparentFolders.value = enhanceFolderNodes(
        state.folderStructure,
        state.loraInfoList
      );

      updateFilteredLoras();
      ElMessage.success(`已加载 ${result.data.loras.length} 个Lora模型`);
    } else {
      ElMessage.error('加载Lora失败: ' + result.message);
    }
  } catch (error) {
    console.error('IPC获取lora失败:', error);
    ElMessage.error("Lora列表加载失败，请检查配置路径");
  } finally {
    state.isLoading = false;
  }
};

// 增强文件夹节点，添加Lora数量统计
const enhanceFolderNodes = (
  folders: FolderNode[],
  loras: LoraInfo[]
): EnhancedFolderNode[] => {
  return folders.map(folder => {
    // 计算当前文件夹下的Lora数量
    const loraCount = loras.filter(lora =>
      lora.grandparent_folder === folder.name ||
      (!lora.grandparent_folder && lora.parent_folder === folder.name)
    ).length;

    // 递归处理子文件夹
    const children = folder.children.map(subfolder => {
      const subLoraCount = loras.filter(lora =>
        lora.grandparent_folder === folder.name &&
        lora.parent_folder === subfolder.name
      ).length;

      return {
        ...subfolder,
        loraCount: subLoraCount,
        children: []
      };
    });

    return {
      ...folder,
      loraCount,
      children
    };
  });
};

// 刷新Lora列表
const refreshList = () => {
  state.loraInfoList = [];
  state.searchTerm = '';
  state.sortBy = 'name-asc';
  clearFilters();
  getLoraList();
};

const openLoraDrawer = ()=>{
  showLoraApply.value = true;
  preloadMode.value = true;
}

// 过滤Lora
const filterLoras = debounce(() => {
  updateFilteredLoras();
}, 300);

// 排序Lora
const sortLoras = () => {
  updateFilteredLoras();
};

// 获取本地图片URL
const getLocalImageUrl = (lora: LoraInfo) => {
  if (!lora.lora_image) return '';

  // 获取版本号，如果没有则创建
  if (imageVersions[lora.lora_path] === undefined) {
    imageVersions[lora.lora_path] = 0;
  }
  return configStore.comfyProxyImage(`file://${lora.lora_image}`);
};

// 处理图片加载错误
const handleImageError = (event: Event) => {
};

// 处理图片加载成功
const handleImageLoad = (event: Event) => {
  const img = event.target as HTMLImageElement;
  img.style.opacity = '1';
};

// 使用节流处理滚动事件
const handleScroll = throttle(() => {
  if (gridContainer.value) {
    scrollTop.value = gridContainer.value.scrollTop;
  }
}, 16); // 约60fps

// 更新列数响应式
const updateColumnCount = () => {
  if (!gridContainer.value) return;

  const containerWidth = gridContainer.value.clientWidth;
  // 根据容器宽度计算列数 (最小200px/列)
  columnCount.value = Math.max(1, Math.floor(containerWidth / 200));
};

// 响应容器大小变化
const setupResizeObserver = () => {
  if (!gridContainer.value) return;

  resizeObserver = new ResizeObserver(entries => {
    for (const entry of entries) {
      if (entry.target === gridContainer.value) {
        containerHeight.value = entry.contentRect.height;
        updateColumnCount();
      }
    }
  });

  resizeObserver.observe(gridContainer.value);
};

const taskStatus = reactive({
  loading: false,
  progressVisible: false,
  progress: 0, 
  taskId: 'lora_global_check',
  progressTimer: null as NodeJS.Timeout | null 
});

const scanAllLora = ()=>{
  clearProgressTimer();
  state.scanLoading = true
  taskStatus.loading = true;

  loraApi().scanAndCompleteLoras().then(res=>{
    if(res.data.success){
      ElMessage.success(JSON.stringify(res.data))
      taskStatus.progressTimer = setInterval(() => {
        fetchTaskProgress();
      }, 800);
    }
    else{
      ElMessage.error(JSON.stringify(res.data))
      resetTaskStatus();
    }
  }).catch(error => {
    ElMessage.error("请求失败：" + (error.message || "网络错误"));
    // 请求失败时，清理状态
    resetTaskStatus();
  });
}

const fetchTaskProgress = async () => {
  try {
    if (!taskStatus.taskId) return;

    const response = await translateApi().getTaskProgress(taskStatus.taskId);
    taskStatus.progress = response.data.progress;

    // 进度完成（100%）或取消（-1）时，清理定时器并隐藏进度条
    if (response.data.progress === 100 || response.data.progress === -1) {
      clearProgressTimer();
      setTimeout(() => {
        taskStatus.progressVisible = false;
        taskStatus.loading = false;
        state.scanLoading = false;
      }, 1000);
    }
  } catch (error) {
    console.error("查询进度失败：", error);
  }
};

// 清除进度定时器（统一封装）
const clearProgressTimer = () => {
  if (taskStatus.progressTimer) {
    clearInterval(taskStatus.progressTimer);
    taskStatus.progressTimer = null;
  }
};

// 重置任务状态（统一封装）
const resetTaskStatus = () => {
  clearProgressTimer();
  taskStatus.progressVisible = false;
  taskStatus.loading = false;
  state.scanLoading = false;
  taskStatus.progress = 0;
};

const civitaiTaskStatus = reactive({
  loading: false,
  progress: 0,
  taskId: 'marvelous_info_global_check', // 新的固定任务ID
  progressTimer: null as NodeJS.Timeout | null
});

// 启动Civitai批量检查任务
const scanAndCompleteCivitai = () => {
  clearCivitaiProgressTimer(); // 先清除可能存在的旧定时器
  state.scanLoading = true;
  civitaiTaskStatus.loading = true;
  civitaiTaskStatus.progress = 0;

  loraApi().scanAndCompleteCivitai().then(res => {
    if (res.data.success) {
      ElMessage.success(res.data.message || "Civitai批量任务已启动");
      // 启动进度查询定时器
      civitaiTaskStatus.progressTimer = setInterval(() => {
        fetchCivitaiTaskProgress();
      }, 800);
    } else {
      ElMessage.error(res.data.message || "启动Civitai任务失败");
      resetCivitaiTaskStatus();
    }
  }).catch(error => {
    ElMessage.error("请求失败：" + (error.message || "网络错误"));
    resetCivitaiTaskStatus();
  });
};

// 查询Civitai任务进度
const fetchCivitaiTaskProgress = async () => {
  try {
    if (!civitaiTaskStatus.taskId) return;

    const response = await translateApi().getTaskProgress(civitaiTaskStatus.taskId);
    civitaiTaskStatus.progress = response.data.progress;

    // 任务完成或取消时清理
    if (response.data.progress === 100 || response.data.progress === -1) {
      clearCivitaiProgressTimer();
      setTimeout(() => {
        civitaiTaskStatus.loading = false;
        state.scanLoading = false;
      }, 1000);
    }
  } catch (error) {
    console.error("Civitai进度查询失败：", error);
  }
};

// 清除Civitai任务定时器
const clearCivitaiProgressTimer = () => {
  if (civitaiTaskStatus.progressTimer) {
    clearInterval(civitaiTaskStatus.progressTimer);
    civitaiTaskStatus.progressTimer = null;
  }
};

// 重置Civitai任务状态
const resetCivitaiTaskStatus = () => {
  clearCivitaiProgressTimer();
  civitaiTaskStatus.loading = false;
  state.scanLoading = false;
  civitaiTaskStatus.progress = 0;
};



onMounted(async() => {
  getLoraList();
  showLoraApply.value = true
  setTimeout(() => {
    showLoraApply.value = false
    console.log('预加载完成，LoraLoader ref:', loraLoaderRef.value)
  }, 200)
  if (gridContainer.value) {
    containerHeight.value = gridContainer.value.clientHeight;
    updateColumnCount();
    setupResizeObserver();
  }
});

onUnmounted(() => {
  if (resizeObserver) {
    resizeObserver.disconnect();
  }
});

// 监听过滤结果变化，确保滚动位置正确
watch(filteredLoras, () => {
  if (gridContainer.value) {
    gridContainer.value.scrollTop = 0;
    scrollTop.value = 0;
  }
});

// 监听搜索词和排序变化
watch([() => state.searchTerm, () => state.sortBy], () => {
  updateFilteredLoras();
});
</script>

<style scoped>
.lora-manager {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 16px;
  background-color: var(--el-bg-color-page);
}

.lora-item.lora-applied {
  border: 2px solid #67c23a;
  box-shadow: 0 0 8px rgba(103, 194, 58, 0.3);
}

.applied-tag {
  margin-left: 8px;
  vertical-align: middle;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
  padding: 16px;
  background: var(--el-bg-color-overlay);
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}

.filters {
  flex: 1;
  min-width: 300px;
}

.folder-filter {
  margin-bottom: 12px;
}

.folder-filter span {
  margin-right: 8px;
  font-size: 14px;
  color: var(--el-text-color-secondary);
}

.folder-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.folder-tag {
  cursor: pointer;
  transition: all 0.3s;
}

.folder-tag:hover {
  opacity: 0.8;
  transform: translateY(-2px);
}

.search-sort {
  display: flex;
  gap: 12px;
  align-items: center;
}

.search-input {
  width: 240px;
}

.sort-select {
  width: 120px;
}

.refresh-btn {
  margin-left: 8px;
}

.stats {
  display: flex;
  gap: 16px;
  margin-bottom: 16px;
  padding: 0 8px;
  font-size: 14px;
  color: var(--el-text-color-secondary);
}

.lora-grid-container {
  flex: 1;
  position: relative;
  overflow-y: auto;
  background: var(--el-bg-color-overlay);
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
  /* 提升滚动性能 */
  will-change: transform;
  contain: strict;
}

.lora-grid-phantom {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: -1;
}

.lora-grid-content {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: grid;
  gap: 16px;
  padding: 16px;
  will-change: transform;
  /* 提升渲染性能 */
  backface-visibility: hidden;
  perspective: 1000;
  transform: translateZ(0);
}

.lora-item {
  display: flex;
  flex-direction: column;
  height: 230px;
  background: var(--el-bg-color-overlay);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s;
  /* 提升渲染性能 */
  transform: translateZ(0);
  backface-visibility: hidden;
}

.lora-item:hover {
  transform: translateY(-5px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}

.image-container {
  position: relative;
  width: 100%;
  height: 160px;
  background-color: var(--el-fill-color-lighter);
  overflow: hidden;
}

.lora-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: opacity 0.3s;
  opacity: 0;
  /* 提升图片渲染性能 */
  content-visibility: auto;
  pointer-events: none;
}

.image-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--el-text-color-placeholder);
  font-size: 48px;
}

.lora-info {
  padding: 12px;
  flex: 1;
}

.lora-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--el-text-color-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lora-path {
  margin-top: 4px;
  font-size: 12px;
  color: var(--el-text-color-placeholder);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.loading-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  color: var(--el-text-color-placeholder);
  font-size: 14px;
}

.loading-icon {
  margin-right: 8px;
  animation: rotate 2s linear infinite;
}

/* 元数据弹窗样式 */
.metadata-container {
  padding: 15px;
}

.metadata-section {
  margin-bottom: 20px;
}

.tag-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
  padding: 10px;
  background-color: var(--el-fill-color-lighter);
  border-radius: 8px;
}

.tag-item {
  padding: 4px 8px;
  border-radius: 4px;
  transition: all 0.3s ease;
  cursor: default;
  background-color: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
}

.tag-frequency {
  font-size: 0.8em;
  margin-left: 4px;
  opacity: 0.7;
}

.tag-selection {
  margin-top: 20px;
}

.preview-container {
  text-align: center;
}

.no-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 150px;
  color: var(--el-text-color-placeholder);
  background-color: var(--el-fill-color-lighter);
  border-radius: 4px;

  .el-icon {
    margin-bottom: 10px;
  }
}

.lora-info {
  margin-top: 15px;
  padding: 10px;
  background-color: var(--el-fill-color-light);
  border-radius: 4px;
  text-align: left;

  & > div {
    margin-bottom: 8px;
  }
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

@media (max-width: 1200px) {
  .lora-grid-content {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 900px) {
  .lora-grid-content {
    grid-template-columns: repeat(2, 1fr);
  }

  .toolbar {
    flex-direction: column;
  }

  .search-sort {
    width: 100%;
  }
}

@media (max-width: 600px) {
  .lora-grid-content {
    grid-template-columns: 1fr;
  }

  .search-input {
    width: 100%;
  }
}
</style>
